package com.ld.zxw.index;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import org.apache.commons.lang3.StringUtils;
import org.apache.log4j.Logger;
import org.apache.lucene.document.Document;
import org.apache.lucene.index.DirectoryReader;
import org.apache.lucene.queryparser.classic.MultiFieldQueryParser;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.ScoreDoc;
import org.apache.lucene.search.Sort;
import org.apache.lucene.search.SortField;
import org.apache.lucene.search.TopDocs;
import org.apache.lucene.search.TopFieldCollector;
import org.apache.lucene.search.highlight.Fragmenter;
import org.apache.lucene.search.highlight.Highlighter;
import org.apache.lucene.search.highlight.InvalidTokenOffsetsException;
import org.apache.lucene.search.highlight.QueryScorer;
import org.apache.lucene.search.highlight.SimpleFragmenter;

import com.alibaba.fastjson.JSONObject;
import com.ld.zxw.config.Config;
import com.ld.zxw.config.DefaultObject;
import com.ld.zxw.config.IndexFactory;
import com.ld.zxw.page.Page;
import com.ld.zxw.util.DateUtil;

public class QueryIndex implements IndexFactory{

	private Logger log = Logger.getLogger(QueryIndex.class);

	/**
	 * 检索集合
	 */
	@Override
	public <T> List<T> findList(Config config, String value,Class<T> obj,int several) {
		DirectoryReader ireader = null;
		try {
			long start_time = DateUtil.getDate();
			ireader = DirectoryReader.open(config.getDirectory());  
			IndexSearcher searcher = new IndexSearcher(ireader);   
			Query query = new MultiFieldQueryParser(queryField(config), config.getAnalyzer()).parse(value);
			TopDocs rs = searcher.search(query,several,sortField(config));
			QueryScorer scorer=new QueryScorer(query);  
			DateUtil.timeConsuming("查询内核耗时", start_time);
			long date = DateUtil.getDate();
			List<T> dataHandle = dataHandle(query,ireader,searcher, rs.scoreDocs, scorer, config, obj);
			DateUtil.timeConsuming("查询数据整理耗时", date);
			return dataHandle;
		} catch (Exception e) {
			log.error("查询索引-error:",e);
			return null;
		}finally {
			if(ireader != null){
				try {
					ireader.close();
				} catch (Exception e2) {
					log.error("关闭流-error:",e2);
				}
			}
		}
	}

	/**
	 * 分页检索
	 */
	@Override
	public <T> Page<T> findPageList(Config config, String value, int pageNumber, int pageSize, Class<T> obj) {
		DirectoryReader ireader = null;
		Page<T> page = null;
		try {
			ireader = DirectoryReader.open(config.getDirectory());  
			IndexSearcher searcher = new IndexSearcher(ireader);   
			Query query = new MultiFieldQueryParser(queryField(config), config.getAnalyzer()).parse(value);
			TopFieldCollector create = TopFieldCollector.create(sortField(config), pageNumber+pageSize, false, false, false);
			searcher.search(query,create);
			int totalHits = create.getTotalHits();
			ScoreDoc[] scoreDocs = create.topDocs(pageNumber,pageSize).scoreDocs;
			QueryScorer scorer = new QueryScorer(query);
			List<T> dataHandle = dataHandle(query, ireader, searcher, scoreDocs, scorer, config, obj);
			int totalPage = (int) (totalHits / pageSize);
			if (totalHits % pageSize != 0) {
				totalPage++;
			}
			page = new Page<T>(dataHandle, pageNumber, pageSize, totalPage, totalHits);
		}catch (Exception e) {
			log.error("分页检索-error:",e);
		}finally {
			if(ireader != null){
				try {
					ireader.close();
				} catch (Exception e2) {
					log.error("关闭流-error:",e2);
				}
			}
		}
		return page;
	}
	
	/**
	 * 检索集合 范围
	 */
	@Override
	public <T> List<T> findRangeList(Config config,Query query,Class<T> obj,int several) {
		DirectoryReader ireader = null;
		try {
			long start_time = DateUtil.getDate();
			ireader = DirectoryReader.open(config.getDirectory());  
			IndexSearcher searcher = new IndexSearcher(ireader);   
			TopDocs rs = searcher.search(query,several,sortField(config));
			QueryScorer scorer=new QueryScorer(query);  
			DateUtil.timeConsuming("查询内核耗时", start_time);
			long date = DateUtil.getDate();
			List<T> dataHandle = dataHandle(query,ireader,searcher, rs.scoreDocs, scorer, config, obj);
			DateUtil.timeConsuming("查询数据整理耗时", date);
			return dataHandle;
		} catch (Exception e) {
			log.error("查询索引-error:",e);
			return null;
		}finally {
			if(ireader != null){
				try {
					ireader.close();
				} catch (Exception e2) {
					log.error("关闭流-error:",e2);
				}
			}
		}
	}
	
	/**
	 * 分页检索
	 */
	@Override
	public <T> Page<T> findRangePageList(Config config,Query query, int pageNumber, int pageSize, Class<T> obj) {
		DirectoryReader ireader = null;
		Page<T> page = null;
		try {
			ireader = DirectoryReader.open(config.getDirectory());  
			IndexSearcher searcher = new IndexSearcher(ireader);   
			TopFieldCollector create = TopFieldCollector.create(sortField(config), pageNumber+pageSize, false, false, false);
			searcher.search(query,create);
			int totalHits = create.getTotalHits();
			ScoreDoc[] scoreDocs = create.topDocs(pageNumber,pageSize).scoreDocs;
			QueryScorer scorer = new QueryScorer(query);
			List<T> dataHandle = dataHandle(query, ireader, searcher, scoreDocs, scorer, config, obj);
			int totalPage = (int) (totalHits / pageSize);
			if (totalHits % pageSize != 0) {
				totalPage++;
			}
			page = new Page<T>(dataHandle, pageNumber, pageSize, totalPage, totalHits);
		}catch (Exception e) {
			log.error("分页检索-error:",e);
		}finally {
			if(ireader != null){
				try {
					ireader.close();
				} catch (Exception e2) {
					log.error("关闭流-error:",e2);
				}
			}
		}
		return page;
	}

	/**
	 * 数据处理
	 * @param query
	 * @param ireader
	 * @param searcher
	 * @param rs
	 * @param scorer
	 * @param config
	 * @param obj
	 * @return
	 * @throws IOException
	 */
	public <T> List<T> dataHandle(Query query,DirectoryReader ireader,IndexSearcher searcher,ScoreDoc[] scoreDocs,QueryScorer scorer,Config config,Class<T> obj) throws IOException{
		List<T> list = new ArrayList<>();
		int length = scoreDocs.length;
		Highlighter highlighter = getHighlighter(config, scorer);
		long bestFragment_time = DateUtil.getDate();
		for (int i = 0; i < length; i++) {
			JSONObject object = new JSONObject();
			Document doc = searcher.doc(scoreDocs[i].doc);
			List<DefaultObject> xmlDem = config.getXmlDem();
			int size = xmlDem.size();
			for (int j = 0; j < size; j++) {
				DefaultObject defaultObject = xmlDem.get(j);
				String name = defaultObject.getName();
				String value = doc.get(name);
				if(config.isHighlight() && defaultObject.isQuery()){
					
					//高亮设置
					String bestFragment = null;
					try {
						
						bestFragment = highlighter.getBestFragment(config.getAnalyzer().tokenStream(name, value), value);
					} catch (InvalidTokenOffsetsException e) {
						log.error("高亮异常:", e);
					}
					if(bestFragment != null){
						object.put(name, bestFragment);
					}else{
						object.put(name, value);
					}
					
				}else{
					object.put(name, value);
				}
			}
			list.add(JSONObject.toJavaObject(object, obj));
		}
		DateUtil.timeConsuming("每条高亮 ---", bestFragment_time);
		return list;
	}


	public Highlighter getHighlighter(Config config,QueryScorer scorer){
		Fragmenter fragmenter = new SimpleFragmenter(100);//默认字符为100   
		Highlighter highlighter = new Highlighter(config.getSimpleHTMLFormatter(),scorer);
		highlighter.setTextFragmenter(fragmenter);
		return highlighter;
	}
	/**
	 * 查询字段解析
	 * @param config 
	 * @return
	 */
	private String[] queryField(Config config){
		List<DefaultObject> xmlDem = config.getXmlDem();
		int size = xmlDem.size();
		String sql = "";
		String[] field = null;
		for (int j = 0; j < size; j++) {
			DefaultObject defaultObject = xmlDem.get(j);
			if(defaultObject.isQuery()){
				sql +=","+defaultObject.getName();
			}

		}
		if(StringUtils.isNotBlank(sql)){
			sql = sql.substring(1, sql.length());
			field = sql.split(",");
		}
		return field;
	}

	/**
	 * 排序器
	 * @param config 
	 */
	private Sort sortField(Config config){
		Sort sort = new Sort();
		List<DefaultObject> xmlDem = config.getXmlDem();
		int size = xmlDem.size();
		for (int i = 0; i <size; i++) {
			DefaultObject e = xmlDem.get(i);
			SortField sortField = null;
			if(StringUtils.isNotBlank(e.getSort())){
				switch (e.getType()) {
				case "int":
					sortField = new SortField(e.getName(), SortField.Type.INT, Boolean.valueOf(e.getSort()));
					break;
				case "float":
					sortField = new SortField(e.getName(), SortField.Type.FLOAT, Boolean.valueOf(e.getSort()));
					break;
				case "double":
					sortField = new SortField(e.getName(), SortField.Type.DOUBLE, Boolean.valueOf(e.getSort()));
					break;
				case "string":
					sortField = new SortField(e.getName(), SortField.Type.STRING, Boolean.valueOf(e.getSort()));
					break;
				default:
					break;
				}
				sort.setSort(sortField);
			}
		}
		return sort;
	}
}
